//
// Created by vell on 2021/11/26.
//
#ifndef V_STRING_UTILS_H
#define V_STRING_UTILS_H

#include <cstdarg>
#include <string>
#include <memory>
#include <cstring>
#include <sstream>

namespace VUtils {
    template<typename ... Args>
    inline std::string strFormat(const std::string &format, Args ... args) {
        size_t size = (size_t) snprintf(nullptr, 0, format.c_str(), args ...) + 1; // Extra space for '\0'
        std::unique_ptr<char[]> buf(new char[size]);
        snprintf(buf.get(), size, format.c_str(), args ...);
        return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
    }

    inline std::string strFmt(const std::string &fmt_str, ...) {
        int final_n, n =
                ((int) fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */
        std::unique_ptr<char[]> formatted;
        va_list ap;
        while (1) {
            formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */
            strcpy(&formatted[0], fmt_str.c_str());
            va_start(ap, fmt_str);
            final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap);
            va_end(ap);
            if (final_n < 0 || final_n >= n)
                n += abs(final_n - n + 1);
            else
                break;
        }
        return std::string(formatted.get());
    }

    inline bool startsWith(const char *str, const char *pre) {
        size_t lenpre = strlen(pre), lenstr = strlen(str);
        return lenstr < lenpre ? false : strncmp(pre, str, lenpre) == 0;
    }

    inline int __base64Encode(unsigned char *pDest, const unsigned char *pSrc, size_t nSrcLen) {
        const static unsigned char s_encTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        unsigned char *dst = pDest;
        const unsigned char *src = pSrc;
        size_t len = nSrcLen;

        unsigned char *odst = dst;
        unsigned long l = 0, m = 0, n = 0;

        // 循环处理分组
        size_t off = 0;
        for (; off + 3 <= len; off += 3) {
            l = *src++;
            m = *src++;
            n = *src++;

            *dst++ = s_encTable[(l >> 2) & 0x3F];
            *dst++ = s_encTable[((l << 4) & 0x30) | ((m >> 4) & 0x0F)];
            *dst++ = s_encTable[((m << 2) & 0x3C) | ((n >> 6) & 0x03)];
            *dst++ = s_encTable[n & 0x3F];
        }

        // 处理余下的2个字节
        if (off + 2 <= len) {
            l = *src++;
            m = *src++;

            *dst++ = s_encTable[(l >> 2) & 0x3F];
            *dst++ = s_encTable[((l << 4) & 0x30) | ((m >> 4) & 0x0F)];
            *dst++ = s_encTable[((m << 2) & 0x3C)];
            *dst++ = '=';
        }

            // 处理余下的1个字节
        else if (off + 1 <= len) {
            l = *src++;

            *dst++ = s_encTable[(l >> 2) & 0x3F];
            *dst++ = s_encTable[((l << 4) & 0x30)];
            *dst++ = '=';
            *dst++ = '=';
        }

        return (int) (dst - odst);
    }

    inline int __base64Decode(unsigned char *pDest, const unsigned char *pSrc, size_t nSrcLen) {
        const static unsigned char s_decTable[] = {
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F,
                0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
                0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
                0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
        };
        unsigned char *dst = pDest;
        const unsigned char *src = pSrc;
        size_t len = nSrcLen;

        unsigned char *odst = dst;
        unsigned long l = 0, m = 0, n = 0, o = 0;

        // 循环处理分组
        size_t off = 0;
        for (; off + 4 <= len; off += 4) {
            if ((src[0] > 0x7F) || (src[1] > 0x7F) || (src[2] > 0x7F) || (src[3] > 0x7F)) return (int) (dst - odst);
            if ((src[0] == '=') || (src[1] == '=') || (src[2] == '=') || (src[3] == '=')) break;

            l = s_decTable[*src++];
            m = s_decTable[*src++];
            n = s_decTable[*src++];
            o = s_decTable[*src++];

            *dst++ = (unsigned char) (((l << 2) & 0xFC) | ((m >> 4) & 0x03));
            *dst++ = (unsigned char) (((m << 4) & 0xF0) | ((n >> 2) & 0x0F));
            *dst++ = (unsigned char) (((n << 6) & 0xC0) | (o & 0x3F));
        }

        // 处理余下的3个字节
        if (off + 3 <= len) {
            if ((src[0] != '=') && (src[1] != '=')) {
                l = s_decTable[*src++];
                m = s_decTable[*src++];

                *dst++ = (unsigned char) (((l << 2) & 0xFC) | ((m >> 4) & 0x03));
            }

            if ((src[2] != '=')) {
                n = s_decTable[*src++];

                *dst++ = (unsigned char) (((m << 4) & 0xF0) | ((n >> 2) & 0x0F));
            }
        }

            // 处理余下的两个字节
        else if (off + 2 <= len) {
            if ((src[0] != '=') && (src[1] != '=')) {
                l = s_decTable[*src++];
                m = s_decTable[*src++];

                *dst++ = (unsigned char) (((l << 2) & 0xFC) | ((m >> 4) & 0x03));
            }
        }

        return (int) (dst - odst);
    }

    inline void base64Encode(const std::string &str, std::string &strEncode) {
        strEncode.resize((str.size() / 3 + 1) * 4);
        strEncode.resize(
                __base64Encode((unsigned char *) strEncode.data(), (const unsigned char *) str.data(), str.size()));
    }

    inline void base64Decode(const std::string &str, std::string &strDecode) {
        strDecode.resize(str.size());
        strDecode.resize(
                __base64Decode((unsigned char *) strDecode.data(), (const unsigned char *) str.data(), str.size()));
    }

    inline double str2double(const std::string &str) {
        std::stringstream s(str);
        double b;
        s >> b;
        return b;
    }
}

#endif //V_STRING_UTILS_H
